﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SPSiteHelper.cs" company="Universal Music Group">
//   All rights reserved
// </copyright>
// <summary>
//   Adds helper methods to the Sharepoint <see cref="SPSite" /> class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace AgileShare.Framework.Library.Helpers
{
    using System;
    using System.Diagnostics;
    using System.Reflection;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    using Microsoft.SharePoint.Administration.Claims;
    using AgileShare.Framework.Core;

    /// <summary>
    /// Adds helper methods to the Sharepoint <see cref="SPSite"/> class.
    /// </summary>
    public static class SPSiteHelper
    {
        //  Public Methods (5) 

        // [rgn] Public Methods (2)
        #region Public Methods

        /// <summary>
        /// Adds a property to the web property bag
        /// It first removes the old value if a value with the same name already exists. 
        /// </summary>
        /// <param name="site">
        /// The site.
        /// </param>
        /// <param name="name">
        /// </param>
        /// <param name="propertyValue">
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        public static void AddProperty<T>(SPSite site, string name, T propertyValue)
        {
            // where T : ISerializable
            try
            {
                SPVerificationHelper.VerifyObjectArgument(site, "site");
                SPVerificationHelper.VerifyStringArgument(name, "name");
            }
            catch (Exception ex)
            {
                string errorMessage = ex.Message;
                TraceProvider.Log(errorMessage, TraceProviderCategories.Default,
                    (uint)TraceProviderEventIds.SPListHelper, TraceProviderSeverity.Information, ex);
                throw;
            }
        }

        public static SPSite CreateSiteCollectionSelfService(SPWeb web, string url, string title, string description, string template, SPUser user)
        {
            SPSite site = web.Site;

            string loginName = string.Empty;
            try
            {
                SPClaimProviderManager mgr = SPClaimProviderManager.Local;
                if (mgr != null)
                {
                    SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Decoding Claim", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, "Claim Decoded", null);
                    loginName = mgr.DecodeClaim(user.LoginName).Value;
                }
                else
                {
                    SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Decoding Claim", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, "Claim Not Required for decoding", null);
                    loginName = user.LoginName;
                }
            }
            catch (Exception ex)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Site Creation Claims Checker", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
                loginName = user.LoginName;
            }

            SPSite sitecollection = null;

            sitecollection = site.SelfServiceCreateSite(
                url,
                title,
                description,
                1033,
                template,
                loginName,
                user.Name,
                user.Email,
                loginName,
                user.Name,
                user.Email);

            SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Site Creation Completed", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, "Site Creation Method Complete", null);

            return sitecollection;
        }


        /// <summary>
        /// This function will create a site collection using <see cref=”SPSite.SelfServiceCreateSite”/>
        /// </summary>
        /// <param name=”relativeSiteUrl”>
        /// The relative url of a site. This should include a managed path since the root site
        /// collection already exist. Example “sites/testsite”
        /// </param>
        /// <param name=”title”>The title of the new site</param>
        /// <param name=”description”>The description of the new site</param>
        /// <param name=”webTemplate”>The web template name</param>
        /// <param name=”secondarySiteCollectionAdministratorLogin”>
        /// The user login account of the secondary site collection administrator
        /// </param>
        /// <param name=”secondarySiteCollectionAdministratorName”>
        /// The name of the secondary site collection administrator
        /// </param>
        /// <param name=”secondarySiteCollectionAdministratorEmail”>
        /// The email address of the secondary site collection administrator
        /// </param>
        /// <returns>
        /// The newly create <see cref=”SPSite”/> or null when an error has occured.
        /// </returns>
        public static SPSite CreateNewSiteCollection(string relativeSiteUrl,
                string title,
                string description,
                string webTemplate,
                string secondarySiteCollectionAdministratorLogin,
                string secondarySiteCollectionAdministratorName,
                string secondarySiteCollectionAdministratorEmail)
        {
            SPSite newSiteCollection = null;

            // The creation will only work if you set the primary administrator is set to the current user
            string primarySiteCollectionAdministratorLogin = SPContext.Current.Web.CurrentUser.LoginName;
            string primarySiteCollectionAdministratorName = SPContext.Current.Web.CurrentUser.Name;
            string primarySiteCollectionAdministratorEmail = SPContext.Current.Web.CurrentUser.Email;
            // Small change you want to set this variable on a different value as the root site collection
            uint localIdentifier = (uint)SPContext.Current.Web.Locale.LCID;

            try
            {
                // Make sure we are at the root site collection
                using (SPSite rootSite = new SPSite(GetAuthorityUrl(SPContext.Current.Web.Url)))
                {
                    // If no secondary site collection administrator is provided, use null values
                    // in stead of an empty string. Otherwise an Exception will be thrown.
                    if (string.IsNullOrEmpty(secondarySiteCollectionAdministratorLogin))
                        newSiteCollection = rootSite.SelfServiceCreateSite(
                                relativeSiteUrl,
                                title,
                                description,
                                localIdentifier,
                                webTemplate,
                                primarySiteCollectionAdministratorLogin,
                                primarySiteCollectionAdministratorName,
                                primarySiteCollectionAdministratorEmail,
                                null,
                                null,
                                null);
                    else
                    {
                        // When a secundary site collection has been provided, add this account as a
                        // secundary site collection administrator
                        newSiteCollection = rootSite.SelfServiceCreateSite(
                                relativeSiteUrl,
                                title,
                                description,
                                localIdentifier,
                                webTemplate,
                                primarySiteCollectionAdministratorLogin,
                                primarySiteCollectionAdministratorName,
                                primarySiteCollectionAdministratorEmail,
                                secondarySiteCollectionAdministratorLogin,
                                secondarySiteCollectionAdministratorName,
                                secondarySiteCollectionAdministratorEmail);
                    }
                }
            }
            catch
            {
                // You can implement some logging here. It can for example happen that you provide
                // a URL of a site that already exist, a template that not exist etc.
            }

            return newSiteCollection;
        }

        /// <summary>
        /// Returns a URL with only the scheme and authority segmenst from a full Url.
        /// This will generate the url of the root site collection
        /// </summary>
        /// <param name="url">The full URL to format.</param>
        /// <returns>Returns the URL without the path and querystring.</returns>
        /// <example>http://devserver</example>
        private static string GetAuthorityUrl(string url)
        {
            Uri uri = new Uri(url);
            return string.Format(@"{0}://{1}", uri.Scheme, uri.Authority);
        }

        public static SPWeb CreateWeb(SPWeb parentWeb, string url, string title, string description, string template)
        {
            SPWeb web = parentWeb.Webs.Add(url, title, description, 1033, template, false, false);

            return web;
        }

        /// <summary>
        /// Creates a new<see cref="!:T:Microsoft.SharePoint.SPSite"/>below the Parent Web application.
        /// </summary>
        /// <param name="webApplication">
        /// The parent webapplication to create the site below.
        /// </param>
        /// <param name="overwrite">
        /// if set to <c>true</c> [overwrite].
        /// </param>
        /// <param name="siteCollectionUrl">
        /// The siteCollection URL.
        /// </param>
        /// <param name="title">
        /// </param>
        /// <param name="description">
        /// </param>
        /// <param name="nLCID">
        /// </param>
        /// <param name="webTemplate">
        /// </param>
        /// <param name="ownerLogin">
        /// </param>
        /// <param name="ownerName">
        /// </param>
        /// <param name="ownerEmail">
        /// </param>
        /// <param name="secondaryContactLogin">
        /// </param>
        /// <param name="secondaryContactName">
        /// </param>
        /// <param name="secondaryContactEmail">
        /// </param>
        /// <param name="databaseServer">
        /// </param>
        /// <param name="databaseName">
        /// </param>
        /// <param name="userName">
        /// The Site owner username.
        /// </param>
        /// <param name="password">
        /// </param>
        /// <param name="useHostHeaderAsSiteName">
        /// </param>
        /// <returns>
        /// The<see cref="!:T:Microsoft.SharePoint.SPSite"/>of the new Site (or existing see "remarks") ; otherwise null if failure.
        /// </returns>
        /// <remarks>
        /// <para>
        /// If the<see cref="!:T:Microsoft.SharePoint.SPSite"/>already exists and overwrite is set to false, then a reference to to the existing SPSite will be returned.
        /// </para>
        /// <para>
        /// Any SPSites returned by this method, need to be Disposed of.
        /// </para>
        /// </remarks>
        public static SPSite CreateSite(
            SPWebApplication webApplication,
            bool overwrite,
            string siteCollectionUrl,
            string title,
            string description,
            uint nLCID,
            string webTemplate,
            string ownerLogin,
            string ownerName,
            string ownerEmail,
            string secondaryContactLogin,
            string secondaryContactName,
            string secondaryContactEmail,
            string databaseServer,
            string databaseName,
            string userName,
            string password,
            bool useHostHeaderAsSiteName)
        {
            SPVerificationHelper.VerifyStringArgument(siteCollectionUrl, "siteCollectionUrl");
            SPVerificationHelper.VerifyStringArgument(ownerLogin, "ownerLogin");
            SPVerificationHelper.VerifyStringArgument(ownerEmail, "ownerEmail");

            try
            {
                bool siteExists = SiteExists(siteCollectionUrl, webApplication);
                if (siteExists && overwrite == false)
                {
                    SPLogger.LogToDeveloper( TraceSeverity.Verbose, MethodInfo.GetCurrentMethod(),  "{0} already exists and override is false. Returning existing SPSite",  siteCollectionUrl);

                    return null;
                }

                if (siteExists)
                {
                    // Delete the site
                    SPLogger.LogToDeveloper( TraceSeverity.Verbose, MethodInfo.GetCurrentMethod(),  "{0} already exists. deleting it first",  siteCollectionUrl);

                    DeleteSiteCollection(webApplication, siteCollectionUrl);
                }

                SPSiteCollection siteCollections = webApplication.Sites;
                SPSite newSite;

                if (!string.IsNullOrEmpty(databaseName))
                {
                    SPVerificationHelper.VerifyStringArgument(databaseServer, "databaseServer");
                    SPVerificationHelper.VerifyStringArgument(webTemplate, "webTemplate");
                    SPVerificationHelper.VerifyStringArgument(title, "title");
                    SPVerificationHelper.VerifyStringArgument(password, "password");
                    SPVerificationHelper.VerifyStringArgument(userName, "userName");
                    SPVerificationHelper.VerifyStringArgument(webTemplate, "webtemplate");

                    // add the site
                    newSite = siteCollections.Add(
                        siteCollectionUrl,
                        title,
                        description,
                        nLCID,
                        webTemplate,
                        ownerLogin,
                        ownerName,
                        ownerEmail,
                        secondaryContactLogin,
                        secondaryContactName,
                        secondaryContactEmail,
                        databaseServer,
                        databaseName,
                        userName,
                        password);

                    return newSite;
                }

                if (!string.IsNullOrEmpty(secondaryContactLogin))
                {
                    SPVerificationHelper.VerifyStringArgument(secondaryContactName, "secondaryContactName");
                    SPVerificationHelper.VerifyStringArgument(secondaryContactEmail, "secondaryContactEmail");
                    SPVerificationHelper.VerifyStringArgument(webTemplate, "webtemplate");

                    newSite = siteCollections.Add(
                        siteCollectionUrl,
                        title,
                        description,
                        nLCID,
                        webTemplate,
                        ownerLogin,
                        ownerName,
                        ownerEmail,
                        secondaryContactLogin,
                        secondaryContactName,
                        secondaryContactEmail,
                        useHostHeaderAsSiteName);

                    return newSite;
                }

                if (!string.IsNullOrEmpty(webTemplate))
                {
                    SPVerificationHelper.VerifyStringArgument(title, "title");

                    newSite = siteCollections.Add(
                        siteCollectionUrl, title, description, nLCID, webTemplate, ownerLogin, ownerName, ownerEmail);

                    return newSite;
                }

                newSite = siteCollections.Add(siteCollectionUrl, ownerLogin, ownerEmail);

                return newSite;
            }
            catch (Exception ex)
            {
                SPLogger.LogToOperations(ex, "SPSiteHelper", "CreateSite");
                throw;
            }
        }

        /// <summary>
        /// Deletes the site.
        /// </summary>
        /// <param name="siteCollectionUrl">
        /// </param>
        /// <returns>
        /// The delete site.
        /// </returns>
        public static bool DeleteSite(string siteCollectionUrl)
        {
            SPVerificationHelper.VerifyStringArgument(siteCollectionUrl, "siteCollectionUrl");

            try
            {
                if (SiteExists(siteCollectionUrl))
                {
                    new SPSite(siteCollectionUrl).Delete();
                }
                else
                {
                    throw new Exception("Site : " + siteCollectionUrl + " doesn't exist.");
                }

                return true;
            }
            catch (Exception ex)
            {
                SPLogger.LogToOperations(ex, "SPSiteHelper", "DeleteSite");
                return false;
            }
        }

        /// <summary>
        /// Deletes the site collection from a specific web application.
        /// </summary>
        /// <param name="webApplication">
        /// The web application.
        /// </param>
        /// <param name="siteCollectionUrl">
        /// The site collection URL.
        /// </param>
        public static void DeleteSiteCollection(SPWebApplication webApplication, string siteCollectionUrl)
        {
            // ok we have to do it this way as there is a bug in the Delte API where if 
            // the url doesn't match ANY url it will try to return one. and then 
            // delete that for you. NICE!
            SPSite site = GetSiteFromWebApp(siteCollectionUrl, webApplication);
            if (site != null)
            {
                site.Delete();
                webApplication.Update();
            }
        }

        /// <summary>
        /// The get current site.
        /// </summary>
        /// <param name="properties">
        /// The properties.
        /// </param>
        /// <returns>
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static SPSite GetCurrentSite(SPFeatureReceiverProperties properties)
        {
            if (properties == null)
            {
                throw new ArgumentNullException("properties");
            }

            SPSite currentSite = null;

            if (properties.Feature.Parent is SPSite)
            {
                currentSite = properties.Feature.Parent as SPSite;
            }

            if (properties.Feature.Parent is SPWeb)
            {
                var currentWeb = properties.Feature.Parent as SPWeb;
                if (currentWeb != null)
                {
                    currentSite = currentWeb.Site;
                }
            }

            if (currentSite == null)
            {
                //SPLogger.LogToOperations( EventLogEntryType.Error, "SPSiteHelper - GetCurrentSite - Unable to Optain an SPSite");
                return currentSite;
            }

            return currentSite;
        }

        /// <summary>
        /// Gets the site.
        /// </summary>
        /// <param name="siteCollectionUrl">
        /// The URL.
        /// </param>
        /// <returns>
        /// SPSite if found; otherwise null;
        /// </returns>
        /// <remarks>
        /// The SPSite returned from this method will need to be disposed by the caller.
        /// </remarks>
        public static SPSite GetSite(string siteCollectionUrl)
        {
            SPSite site = null;
            SPVerificationHelper.VerifyStringArgument(siteCollectionUrl, "siteCollectionUrl");
            try
            {
                if (SiteExists(siteCollectionUrl))
                {
                    site = new SPSite(siteCollectionUrl);
                }

                return site;
            }
            catch (Exception ex)
            {
                string errorMessage = string.Format("Site Not Found:{0}", siteCollectionUrl);
                TraceProvider.Log(errorMessage, TraceProviderCategories.Default, 
                    (uint)TraceProviderEventIds.SPListHelper, TraceProviderSeverity.Information, ex);
                return null;
            }
        }

        /// <summary>
        /// The get site from web app.
        /// </summary>
        /// <param name="siteRelativeUrl">
        /// The site relative url.
        /// </param>
        /// <param name="targetWebApp">
        /// The target web app.
        /// </param>
        /// <returns>
        /// </returns>
        public static SPSite GetSiteFromWebApp(string siteRelativeUrl, SPWebApplication targetWebApp)
        {
            foreach (string name in targetWebApp.Sites.Names)
            {
                if (name.ToLower() == siteRelativeUrl.ToLower())
                {
                    return targetWebApp.Sites[name];
                }
            }

            return null;
        }

        /// <summary>
        /// Removes a property from the property bag
        /// It first removes the old value if a value with the same name already exists. 
        /// </summary>
        /// <param name="site">
        /// The site.
        /// </param>
        /// <param name="name">
        /// The name of the property to be removed
        /// </param>
        public static void RemoveProperty(SPSite site, string name)
        {
            try
            {
                SPVerificationHelper.VerifyObjectArgument(site, "site");
                SPVerificationHelper.VerifyStringArgument(name, "name");
            }
            catch (Exception ex)
            {
                string errorMessage = ex.Message;
                TraceProvider.Log(errorMessage, TraceProviderCategories.Default,
                    (uint)TraceProviderEventIds.SPListHelper, TraceProviderSeverity.Information, ex);
                throw;
            }
        }

        /// <summary>
        /// Tests to see if an SPSite actually exists at the given URL.
        /// </summary>
        /// <param name="siteCollectionUrl">
        /// The URL. e.g. 'site2'
        /// </param>
        /// <returns>
        /// true if site exist; otherwise false if it doesn't exist or an exception occurs.
        /// </returns>
        /// <remarks>
        /// This uses SPWeb.Exists to test to see if the Web Exists or not.
        /// </remarks>
        public static bool SiteExists(string siteCollectionUrl)
        {
            SPVerificationHelper.VerifyStringArgument(siteCollectionUrl, "siteCollectionUrl");

            if (SPSite.Exists(new Uri(siteCollectionUrl)))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Checks to see if a Site Exists in the TargetWebApplication
        /// </summary>
        /// <param name="siteRelativeUrl">
        /// The site relative URL.
        /// </param>
        /// <param name="targetWebApp">
        /// The target web app.
        /// </param>
        /// <returns>
        /// The site exists.
        /// </returns>
        public static bool SiteExists(string siteRelativeUrl, SPWebApplication targetWebApp)
        {
            SPVerificationHelper.VerifyStringArgument(siteRelativeUrl, "siteCollectionUrl");
            SPVerificationHelper.VerifyObjectArgument(targetWebApp, "targetWebApp");

            SPSite site = GetSiteFromWebApp(siteRelativeUrl, targetWebApp);
            if (site != null)
            {
                site.Dispose();
                return true;
            }

            return false;
        }

        public static bool IsCentralAdmin(this SPWeb web)
        {
            return web.IsRootWeb &&
                web.WebTemplate == "CENTRALADMIN" &&
                web.Site.WebApplication.IsAdministrationWebApplication;
        }

        #endregion
    }
}